package com.mudrov.primenumbers;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

public class Executor {

    private static Logger logger = Logger.getLogger(Executor.class);
    private static int n;
    private static int start;
    private static int end;
    private static AtomicInteger started = new AtomicInteger(0);
    private static int sqrtLimit;
    private static int countToDo;
    private static volatile PrimeNumbersGetter primeNumbersGetter;
    private static CountDownLatch countDownLatch ;
    public static void main(String args[]) {
        readParameters();
        long timeout = System.currentTimeMillis();
        List<Integer> primes = getPrimeNumbersIntegersThreadPool();
        timeout = System.currentTimeMillis() - timeout;
        System.out.println("ThreadPool");
        System.out.println("Find " + primes.size() + " prime numbers");
        System.out.println("It takes " + timeout + "ms");
        timeout = System.currentTimeMillis();
        primes = getPrimeNumbersIntegers();
        timeout = System.currentTimeMillis() - timeout;
        System.out.println("Ordinary");
        System.out.println("Find " + primes.size() + " prime numbers");
        System.out.println("It takes " + timeout + "ms");
        //for (Integer i : primes)
            //System.out.print(i + " ");
    }

    private static void readParameters() {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            System.out.println("Please enter lower bound");
            start = Integer.parseInt(reader.readLine());
            System.out.println("Please enter upper bound");
            end = Integer.parseInt(reader.readLine());
            System.out.println("Please enter thread count");
            n = Integer.parseInt(reader.readLine());
        } catch (IOException e) {
            logger.log(Level.FATAL,"Exceptions happen!", e);
            throw new IllegalArgumentException("Illegal user input");
        }
        sqrtLimit = (int) Math.sqrt(end);
        countToDo = sqrtLimit / n;
        primeNumbersGetter = new PrimeNumbersGetter(end);
        countDownLatch  = new CountDownLatch (n);
    }

    private static List<Integer> getPrimeNumbersIntegersThreadPool() {
        ExecutorService taskExecutor = Executors.newFixedThreadPool(n);
        for (int i = 0; i < n; ++i) {
            taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    int threadNumber = started.getAndIncrement();
                    int beginFrom = countToDo * threadNumber;
                    int end = beginFrom + countToDo;
                    primeNumbersGetter.executeAtkinBlock(beginFrom, threadNumber == n - 1? sqrtLimit + 1 : end);
                }
            });
        }
        taskExecutor.shutdown();
        while (!taskExecutor.isTerminated()) {
        }
        primeNumbersGetter.lastPhase();
        return primeNumbersGetter.getSetPrimeNumbers(start);
    }
    private static List<Integer> getPrimeNumbersIntegers() {
        for (int i = 0; i < n; ++i) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int threadNumber = started.getAndIncrement();
                    int beginFrom = countToDo * threadNumber;
                    int end = beginFrom + countToDo;
                    primeNumbersGetter.executeAtkinBlock(beginFrom, threadNumber == n - 1? sqrtLimit + 1 : end);
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            logger.log(Level.FATAL,e);
            return null;
        }
        primeNumbersGetter.lastPhase();
        return primeNumbersGetter.getSetPrimeNumbers(start);
    }
}